﻿//полного решения у меня нет. Нужно думать, может быть ты подскажешь.

//Смотри, конкретно чтоб устранить эту проблему, нужно в каком-то месте составного актора сделать так:

//InPort boostIn=boost.GetInPortByName("ScaleD.inport");

//int indx=boostIn.AllocateNewEntry();
//Т.е. выделить во входном порте бустового актора вход.

//А далее делать просто:

//List<IEntry> d = inport.Get();
//boostIn.Put(indx, d[indx].data); 

//Как видишь тут мы работаем только с 1 входом - indx. Но т.к. число входов в inport может меняться по ходу программы, то мы должны это где-то отслеживать - Как это сделать я не знаю. Так что пока нужно оговориться, что входной порт W(s) - это single порт, т.е. порт только с одним входом.

/*
 * Актор - общая передаточная функция
 * */
using System;
using System.Collections.Generic;
using System.Text;

using ctem.kernel;
using ctem.kernel.lib;
using ctem.kernel.lib.ports.sequential;

//подробные комментарии в файле ConsoleActor.cs

namespace ctem.samples.actors.sequential
{
    public class CommonW : Actor
    {
        //const int m = 1;
        //const int n=2;
        int m;
        int n;
        //DoubleParam m = new DoubleParam("m");
        //DoubleParam n = new DoubleParam("n");

        OutPort outport;
        InPort inport;

        //DoubleParam[] numerator = new DoubleParam[n];
        //DoubleParam[] denumerator = new DoubleParam[n];
        StringParam str_num = new StringParam("strb");
        StringParam str_denom = new StringParam("stra");

        List<double> numerator = new List<double>();
        List<double> denumerator = new List<double>();

        InPort lipa;
        int indx;

        GraphLib.Graph<IActor, ILink> internalGraph = new GraphLib.Graph<IActor, ILink>();
	    List<IActor> sorted_actors = new List<IActor>();
        Booster scaleD;
        double d;

        public CommonW(string name)
            : base(name)
        {
            outport = new OutPort("outport", this);
            base.RegisterOutport(outport);
            inport = new InPort("inport", this);
            base.RegisterInport(inport);

            base.RegisterParam(str_num);
            base.RegisterParam(str_denom);
            str_num.value = "1,0|";
            str_denom.value = "1,0|2,0|";

            ParseStrParam();

        }


        public void ParseStrParam()
        {
            numerator.Clear();
            denumerator.Clear();

            string string_num = ((string)str_num.value).Replace('.', ',');
            string[] temp_num = string_num.Split('|');
            // "1,2|3,4|".Split() => ["1,2";"3,4";""] => длина на один больше
            Array.Reverse(temp_num);
            m = temp_num.GetLength(0)-1;
            for (int i = 1; i < m+1; i++)
                numerator.Add(Convert.ToDouble(temp_num[i]));

            string string_denom = ((string)str_denom.value).Replace('.', ',');
            string[] temp_denom = string_denom.Split('|');
            // "1,2|3,4|".Split() => ["1,2";"3,4";""] => длина на один больше
            Array.Reverse(temp_denom);
            n = temp_denom.GetLength(0)-1;
            for (int i = 1; i < n+1; i++)
                denumerator.Add(Convert.ToDouble(temp_denom[i]));


        }

        public void CreateInnerGraph()
        {
            this.internalGraph = new GraphLib.Graph<IActor,ILink>();
            if (n == 1)
            {
                if (numerator[0] != denumerator[0])
                {
                    Booster boost = new Booster("ScaleD");
                    boost.GetParamByName("k").value = numerator[0] / denumerator[0];
                    //Console.WriteLine("k=" + boost.GetParamByName("k").value);
                    InPort boost_in = (InPort)boost.GetInPortByName("ScaleD.inport");
                    indx = boost_in.AllocateNewEntry();
                    AddNewInternalActor(boost);

                    // создаю липовый входно порт, чтобы решить проблему с передачей данных
                    // он меня спасёт, так как у него есть методы Put и Get
                    lipa = new InPort("lipa", this);

                    // соединяю вых. порт последнего элемента в созданной схеме
                    // с липовым входным портом 
                    Link l_with_lipa = new Link(boost.GetOutPortByName("ScaleD.outport"), lipa);
                    this.internalGraph.AddEdge(l_with_lipa);
                }
            }
            else
            {
                //if (m < n) d = 0; else d = numerator[0] / denumerator[0];
                d = numerator[0] / denumerator[0];
                int order = n - 1;

                Booster boostMain = new Booster("ScaleMain");
                boostMain.GetParamByName("k").value = 1.0;
                indx = boostMain.GetInPorts()[0].AllocateNewEntry();
                AddNewInternalActor(boostMain);

                Accumulator inputAdder = new Accumulator("InputAdder");
                //foreach (InPort inp in inputAdder.GetInPorts())
                //indx = inputAdder.GetInPortByName("InputAdder.inport_plus").AllocateNewEntry();
                AddNewInternalActor(inputAdder);
                Accumulator outputAdder = new Accumulator("OutputAdder");
                //foreach (InPort inp in outputAdder.GetInPorts())
                //    inp.AllocateNewEntry();
                AddNewInternalActor(outputAdder);


                Link MainBoostWithInputAdder = new Link(boostMain.GetOutPorts()[0], inputAdder.GetInPortByName("InputAdder.inport_plus"));
                this.internalGraph.AddEdge(MainBoostWithInputAdder);

                Integrator[] integrators = new Integrator[order];
                //    //IORelation[] nodes = new IORelation[order];
                Link[] nodes = new Link[order];
                Booster[] feedback = new Booster[order];
                Booster[] feedforward = new Booster[order];

                for (int i = 0; i < order; i++)
                {
                    // The integrator names are d0x, d1x, etc.
                    integrators[i] = new Integrator("Integrator" + i.ToString());
                    AddNewInternalActor(integrators[i]);

                    feedback[i] = new Booster("Feedback" + i.ToString());
                    feedback[i].GetParamByName("k").value = -denumerator[i + 1] / denumerator[0];
                    AddNewInternalActor(feedback[i]);

                    feedforward[i] = new Booster("Feedforward" + i.ToString());
                    if (i+1 >= m)
                        feedforward[i].GetParamByName("k").value = d * denumerator[i + 1] / denumerator[0];
                    else
                        feedforward[i].GetParamByName("k").value = (numerator[i + 1] - (d * denumerator[i + 1])) / denumerator[0];
                    AddNewInternalActor(feedforward[i]);

                    // connections
                    nodes[i] = new Link(integrators[i].GetOutPortByName("Integrator" + i.ToString() + ".outport"), feedforward[i].GetInPortByName("Feedforward" + i.ToString() + ".inport"));
                    this.internalGraph.AddEdge(nodes[i]);
                    ///feedback[i].input.link(nodes[i]);
                    Link lll1 = new Link(integrators[i].GetOutPortByName("Integrator" + i.ToString() + ".outport"), feedback[i].GetInPortByName("Feedback" + i.ToString() + ".inport"));
                    this.internalGraph.AddEdge(lll1);

                    Link l1 = new Link(feedback[i].GetOutPortByName("Feedback" + i.ToString() + ".outport"), inputAdder.GetInPortByName("InputAdder.inport_plus"));
                    this.internalGraph.AddEdge(l1);
                    Link l2 = new Link(feedforward[i].GetOutPortByName("Feedforward" + i.ToString() + ".outport"), outputAdder.GetInPortByName("OutputAdder.inport_plus"));
                    this.internalGraph.AddEdge(l2);

                    if (i >= 1)
                    {
                        Link link_integr = new Link(integrators[i - 1].GetOutPortByName("Integrator" + (i - 1).ToString() + ".outport"), integrators[i].GetInPortByName("Integrator" + i.ToString() + ".inport"));
                        this.internalGraph.AddEdge(link_integr);

                        //integrators[i].input.link(nodes[i - 1]);
                        //Link l3 = new Link(integrators[i].GetInPorts()[0], outputAdder.GetInPortByName("Feedback" + i.ToString() + "inport_plus"));
                    }
                }
                Link ll1 = new Link(inputAdder.GetOutPorts()[0], integrators[0].GetInPorts()[0]);
                this.internalGraph.AddEdge(ll1);

                //IORelation inputRelation = (IORelation) connect(input,
                //        inputAdder.plus, "inputRelation");
                //connect(output, outputAdder.output, "outputRelation");

                if (d != 0)
                {
                    scaleD = new Booster("ScaleD");
                    scaleD.GetParamByName("k").value = d;
                    AddNewInternalActor(scaleD);

                    //scaleD.input.link(inputRelation);
                    Link ll4 = new Link(boostMain.GetOutPorts()[0], scaleD.GetInPorts()[0]);
                    this.internalGraph.AddEdge(ll4);
                    //connect(scaleD.output, outputAdder.plus);
                    Link ll2 = new Link(scaleD.GetOutPorts()[0], outputAdder.GetInPortByName("OutputAdder.inport_plus"));
                    this.internalGraph.AddEdge(ll2);
                }

                // создаю липовый входно порт, чтобы решить проблему с передачей данных
                // он меня спасёт, так как у него есть методы Put и Get
                lipa = new InPort("lipa", this);

                // соединяю вых. порт последнего элемента в созданной схеме
                // с липовым входным портом 
                Link l_with_lipa = new Link(outputAdder.GetOutPorts()[0], lipa);
                this.internalGraph.AddEdge(l_with_lipa);

            }
            
        }

        public void AddNewInternalActor(IActor actor)
        {
            foreach (IOutPort p in actor.GetOutPorts())
            {
                p.SetGraph(this.internalGraph);
                this.internalGraph.AddVertex(actor);
            }
        }



        public override void Fire(double step)
        {
            List<IEntry> d = inport.Get();
            if (d.Count > 0)
            {
                
                if (sorted_actors.Count > 0)
                {
                    //Console.WriteLine("sorted_actors.Count > 0");

                    // вручную перемещаю данные с входного порта 
                    // на входной порт первого элемента в созданной схеме
                    sorted_actors[0].GetInPorts()[0].Put(indx, d[0].data);

                    foreach (IActor actor in sorted_actors)
                        actor.Fire(step);


                    // перемещаю данные с липового входного порта в выходной порт составного актора
                    //List<IEntry> d_boost = boost.GetOutPorts()[0].Get();
                    List<IEntry> d_lipa = lipa.Get();
                    outport.Put(d_lipa[0].data.value);
                }
                else
                    outport.Put(d[0].data.value);

            }
        }

        public override void ReInitialize()
        {
            base.ReInitialize();
            ParseStrParam();
            CreateInnerGraph();
            sorted_actors = this.internalGraph.TopologSort();
        }


    }
}
